package de.g18.it0a.jlaser.solution;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import de.g18.it0a.jlaser.domain.LaserConfiguration;
import de.g18.it0a.jlaser.domain.Position;
import de.g18.it0a.jlaser.domain.RoomConfiguration;


public class BruteforceSolutionManager implements SolutionManager {

    private final RoomConfiguration roomConfig;

    private List<SolutionStepListener> stepListeners;


    public BruteforceSolutionManager(RoomConfiguration aRoomConfig) {
        roomConfig = aRoomConfig;
        stepListeners = new ArrayList<SolutionStepListener>();
    }

    @Override
    public LaserConfiguration solve() {
        Vector<Position> unusedMirrorPositions = roomConfig.getPossibleMirrorPositions();
        LaserConfiguration startConfig = new LaserConfiguration(roomConfig.getLaserPosition(), null, new Vector<Position>());
        return findBestSolution(startConfig, unusedMirrorPositions);
    }

    private LaserConfiguration findBestSolution(LaserConfiguration aLaserConfig, Vector<Position> aUnusedMirrorPositions) {
        return findBestSolution(aLaserConfig, aUnusedMirrorPositions, null);
    }

    private LaserConfiguration findBestSolution(LaserConfiguration aLaserConfig, Vector<Position> aUnusedMirrorPositions, LaserConfiguration aBestSolution) {
        if (aBestSolution != null && aLaserConfig.getMirrors().size() >= aBestSolution.getMirrors().size()) {
            return aBestSolution;
        }

        notifyListeners(aLaserConfig);

        if (new SolutionChecker(roomConfig, aLaserConfig).isSolution()) {
            return aLaserConfig;
        } else if (aUnusedMirrorPositions.size() == 1) {
            return aBestSolution;
        }

        LaserConfiguration ownBestSolution = aBestSolution;
        Position lastPosition = aLaserConfig.getEnd() == null ? aLaserConfig.getStart()
                                                              : aLaserConfig.getEnd();
        for (Position possibleEndPosition : aUnusedMirrorPositions) {
            if (lastPosition.getX() == possibleEndPosition.getX()
                 || lastPosition.getY() == possibleEndPosition.getY()) {
               continue;
            }
            if (!SolutionHelper.getInstance(roomConfig).isValidConnection(lastPosition, possibleEndPosition)) {
                continue;
            }
            Vector<Position> localAvailablePositionsCopy = new Vector<Position>(aUnusedMirrorPositions);
            localAvailablePositionsCopy.remove(possibleEndPosition);

            Vector<Position> localMirrorCopy = new Vector<Position>(aLaserConfig.getMirrorPositions());
            if (aLaserConfig.getEnd() != null) {
                localMirrorCopy.add(aLaserConfig.getEnd());
            }
            LaserConfiguration newConfig = new LaserConfiguration(aLaserConfig.getStart(), possibleEndPosition, localMirrorCopy);

            if (ownBestSolution != null && localMirrorCopy.size() >= ownBestSolution.getMirrors().size()) {
                return ownBestSolution;
            }

            LaserConfiguration foundSolution = findBestSolution(newConfig, localAvailablePositionsCopy, ownBestSolution);
            if (foundSolution != null) {
                if (ownBestSolution == null || ownBestSolution.getMirrors().size() > foundSolution.getMirrors().size()) {
                    ownBestSolution = foundSolution;
                }
            }
        }
        return ownBestSolution;
    }

    private void notifyListeners(LaserConfiguration aLaserConfig) {
        for (SolutionStepListener listener : stepListeners) {
            listener.stepPerformed(aLaserConfig);
        }
    }

    @Override
    public void addSolutionStepListener(SolutionStepListener aListener) {
        stepListeners.add(aListener);
    }
}
